﻿using HAF.Dispatcher.Enum;
using HAF.FtpUtility;
using HAF.FtpUtility.Model;
using HAF.LogUtility;
using HAF.LogUtility.Assistantor;
using HAF.LogUtility.Except;
using HAF.LogUtility.Model;
using Iptv.ResourceDowner.Model;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using System.Threading;

namespace Iptv.ResourceDowner.Helper
{
    /// <summary>
    /// M3U8文件下载处理
    /// </summary>
    public class M3u8TaskAssistant : PushMessageable, IDisposable
    {
        M3u8TaskModel _runTask;
        string _taskNum = string.Empty;
        string taskUrl = string.Empty;
        SiteTaskConfig _siteConfig;
        /// <summary>
        /// 下载队列文件
        /// </summary>
        List<SiteFileStruct> _downQueueFiles = new List<SiteFileStruct>();

        /// <summary>
        /// 文件下载完成事件输出
        /// </summary>
        public Action<string, int, string> TaskEndEvent;

        #region 构造函数
        /// <summary>
        /// m3u8文件地址
        /// </summary>
        /// <param name="task"></param>
        public M3u8TaskAssistant(M3u8TaskModel task)
        {
            _taskNum = Guid.NewGuid().ToString("N");
            _runTask = task;
            taskUrl = task.M3u8Path;
            _siteConfig = FtpUrlParser.ParseFtpUrl(_runTask.M3u8Url);
            LogOutName = _runTask.ID;
            ShowDebugMessage("主任务{2}创建M3U8文件{1}的TS子集下载任务{0}", _taskNum, taskUrl, _runTask.TaskID);
            //LogHelper.WriteDebugLogEx(_runTask.TaskID, "创建M3U8的TS子集下载任务{0}", _taskNum);
        }

        /// <summary>
        /// m3u8-ts集合
        /// </summary>
        /// <param name="tsTasks"></param>
        public M3u8TaskAssistant(List<SiteFileStruct> tsTasks)
        {
            _downQueueFiles = tsTasks;
        }
        #endregion 构造函数

        /// <summary>
        /// 下载子集
        /// </summary>
        public void Exec()
        {
            if (!string.IsNullOrEmpty(taskUrl))
            {
                //采集m3u8文件中所有待下载TS集合
                GetM3u8Content(taskUrl);
            }

            //如果存在待处理集合
            if (_downQueueFiles != null && _downQueueFiles.Count > 0)
            {
                if (_runTask.AllowMultiDown)
                {
                    //两个INT相除是没有小数的，必须显示转换为有小数的类型才能除出小数来，否则直接向下取整了
                    double num1 = (double)_downQueueFiles.Count / _runTask.MaxDownThread;
                    //向上取整
                    double num2 = Math.Ceiling(num1);
                    //转换为整数
                    int runNum = Convert.ToInt32(num2);
                    PushDebugLog(LogOutName, "主任务{5}待下载集合总数量为{0}, 分为{1}组进行下载,每组数量为 原始 {2} 取整 {3} -{4}", 
                        _downQueueFiles.Count, _runTask.MaxDownThread, num1, num2, runNum, _taskNum);
                    Dictionary<int, List<SiteFileStruct>> cacheLst = new Dictionary<int, List<SiteFileStruct>>();
                    var runDownlst = new List<SiteFileStruct>();
                    int j = 1;
                    //LogHelper.WriteDebugLogEx(_runTask.TaskID, "待下载集合总数量为{0}, 分为{1}组进行下载, 每组最大数量为{2}", _downQueueFiles.Count, _runTask.MaxDownThread, runNum);
                    for (int i = 0; i < _downQueueFiles.Count; i++)
                    {
                        //LogHelper.WriteDebugLogEx(_runTask.TaskID, "待下载集合总数量为{0}, 目前i编号为{1}， j编号为{2}", _downQueueFiles.Count, i, j);
                        if (j < runNum)
                        {
                            runDownlst.Add(_downQueueFiles[i]);
                            j++;
                        }
                        else
                        {
                            runDownlst.Add(_downQueueFiles[i]);
                            j = 1;
                            //List的复制 方法一
                            List<SiteFileStruct> tmpFiles = new List<SiteFileStruct>(runDownlst.ToArray());
                            //List的复制 方法二
                            //List<SiteFileStruct> temFiles = new List<SiteFileStruct>();
                            //runDownlst.ForEach(t => temFiles.Add(t));

                            cacheLst.Add(i, tmpFiles);
                            runDownlst.Clear(); //会不会把上一个任务的数据清理了？
                        }
                    }
                    var lastFiles = new List<SiteFileStruct>(runDownlst.ToArray());
                    cacheLst.Add(_downQueueFiles.Count, lastFiles);
                    runDownlst.Clear();

                    WriteDebugMessage("主任务{2}待下载集合总数量为{0}, 分为{1}组启动下载线程", _downQueueFiles.Count, cacheLst.Count, _runTask.TaskID);
                    foreach (var item in cacheLst)
                    {
                        DownTasks(item.Value, item.Key);
                        Thread.Sleep(12000);  //等待12秒
                    }

                    bool runable = true;
                    int k = 1;
                    while (runable)
                    {
                        //无匹配的值时返回空列表，不会是NULL，不用考虑NULL的场景
                        List<SiteFileStruct> tmpLst = _downQueueFiles.FindAll(t => t.Status == 1 || t.Status == 0);
                        runable = tmpLst.Count > 0;

                        if (k >= 4)
                        {
                            WriteWarnMessage("当前主任务{0}的执行队列中还有{1}支线任务排队或执行中", _runTask.TaskID, tmpLst.Count);
                            k = 1;
                        }

                        if (!runable)
                        {
                            WriteWarnMessage("当前主任务{0}的执行队列共{2}个工作项，还有{1}支线任务排队或执行中", _runTask.TaskID, tmpLst.Count, _downQueueFiles.Count);
                        }

                        Thread.Sleep(60000);  //等待1分钟 1 * 60 * 1000
                        k++;
                    }

                    SubTaskEnd((int)EnumRunItemStatu.End, string.Format("任务{0}的支线任务完成", _runTask.TaskID));
                }
                else
                {
                    foreach (var item in _downQueueFiles)
                    {
                        ShowDebugMessage("开始下载M3u8文件{0}列表中的{1}", taskUrl, item.RemoteFileName);
                        DownTask(item.ResSaveFullName, item.RemoteFullName, item.ID);
                        Thread.Sleep(12000);
                    }
                }
                
            }
        }

        /// <summary>
        /// 下载线程
        /// </summary>
        /// <param name="runFiles"></param>
        /// <param name="i"></param>
        private void DownTasks(List<SiteFileStruct> runFiles, int i)
        {
            string subTaskId = Guid.NewGuid().ToString("N");
            WriteDebugMessage("当前线程{1}有{0}个下载任务准备下载", runFiles.Count, subTaskId);
            
            Thread tmpThread = new Thread(delegate () {
                DownSubMediaThread(runFiles, subTaskId);
            }) { IsBackground = true, Name = string.Concat("m3u8Thread",i.ToString())};
            tmpThread.Start();
        }

        private void DownSubMediaThread(List<SiteFileStruct> runFiles, string taskId)
        {
            try
            {
                //设置队列指定对象为执行中
                runFiles.ForEach(delegate (SiteFileStruct t) { SetDownStatus(t.Tag, 1); });
                //启动批量下载
                using (FluentFtpHelper ftpClient = new FluentFtpHelper(_siteConfig))
                {
                    ftpClient.LogOutName = _runTask.ID;
                    ftpClient.WriteConsoleMessageEvent = WriteConsoleMessageEvent;
                    //ftpClient.DownloadProcessChangeEvent = WriteProcessEvnet;
                    //ftpClient.DownloadChangeEvent = DownloadChangeEvent;
                    ftpClient.DownloadEndEvent = GatherEnd;
                    ResponsePackage result = ftpClient.DownPatchFiles(runFiles, taskId);
                }
            }
            catch (Exception ex)
            {
                WriteErrMessage(ex.ToString());
            }
            
        }

        object myObject = new object();
        /// <summary>
        /// 设置对象执行状态
        /// </summary>
        /// <param name="fileId"></param>
        /// <param name="stat"></param>
        private void SetDownStatus(string fileId, int stat)
        {
            //多线程处理
            lock (myObject)
            {
                _downQueueFiles.ForEach(delegate (SiteFileStruct t)
                {
                    if (t.ID == fileId)
                    {
                        t.Status = stat;
                    }
                });
            }
            
        }

        /// <summary>
        /// 下载线程
        /// </summary>
        /// <param name="localPath"></param>
        /// <param name="remotePath"></param>
        /// <param name="taskID"></param>
        private void DownTask(string localPath, string remotePath, string taskID)
        {
            ShowDebugMessage("开始下载M3u8文件{0}列表中的{1}", remotePath, localPath);

            using (FluentFtpHelper ftpClient = new FluentFtpHelper(_siteConfig))
            {
                ftpClient.LogOutName = _runTask.TaskID;
                //ftpClient.WriteConsoleMessageEvent = WriteConsolMessageEvent;
                //ftpClient.DownloadProcessChangeEvent = WriteProcessEvnet;
                //ftpClient.DownloadChangeEvent = DownloadChangeEvent;
                ftpClient.DownloadEndEvent = GatherEnd;
                ftpClient.DownFile(localPath, remotePath, taskID);
            }
        }

        /// <summary>
        /// 任务采集完成处理
        /// </summary>
        /// <param name="taskId">下载任务编号</param>
        /// <param name="state">下载任务状态</param>
        private void GatherEnd(string taskId, int state)
        {
            SetDownStatus(taskId, state);

            PushInfoLog(LogOutName, "支线程任务{0}完成，状态为{1}", taskId, state);
        }

        private void SubTaskEnd(int code, string message)
        {
            TaskEndEvent?.Invoke(_runTask.TaskID, code, message);
        }

        private void GetM3u8Content(string url)
        {
            WriteInfoMessageExt(LogOutName, "加载并解析本地M3U8文件{0}", url);
            string m3u8Content = FileAssistant.ReadAllText(url);
            if (!string.IsNullOrEmpty(m3u8Content))
            {
                long duration = 0L;
                int tsCount = 0;
                //文件有效性判断
                if (m3u8Content.ToLower().StartsWith("#EXTM3U".ToLower()))
                {
                    foreach (string line in m3u8Content.Split('\n'))
                    {
                        //获取文件时长
                        if (line.Contains("#EXTINF:"))
                        {
                            string timeStr = Regex.Match(line, @"\d+").Value;
                            //string strTime = line.Substring(8, line.Length - 9);
                            //WriteDebugMessageExt(LogOutName, "timeStr {0} strTime {1}", timeStr, strTime);
                            double doubleDuration = double.Parse(timeStr);
                            duration += (long)doubleDuration;
                        }

                        if (line.Trim().ToLower().EndsWith(".TS".ToLower()))
                        {
                            string localPath = Path.Combine(_runTask.LocalDirectory, line);
                            string remotePath = string.Format("{0}/{1}", _siteConfig.RemotePath, line);
                            string remoteFullName = string.Format("{0}/{1}/{2}", _siteConfig.RemotePath, _siteConfig.RemotePath, line);
                            _downQueueFiles.Add(new SiteFileStruct()
                            {
                                Tag = _runTask.Tag,
                                ID = Guid.NewGuid().ToString("N"),            //工单流水号，可用于日志输出 如_runTask.TaskID 但多任务下载时此处使用GUID
                                Group = "",
                                Name = line,                                  //文件名（无路径）da982dd264d2cb6eaa24db4c8ba32b58
                                FileName = line,                              //文件名（含路径）da982dd264d2cb6eaa24db4c8ba32b58.mp4
                                ResExtName = Path.GetExtension(line),         //文件扩展名 .mp4
                                RemoteFullName = remoteFullName,                    //站点文件全路径  ftp://huaweiftp:Mediafile123!@182.131.0.235:21/20240511/da982dd264d2cb6eaa24db4c8ba32b58.mp4
                                RemoteFileName = remotePath,                      //不含站点信息文件路径 20240511/da982dd264d2cb6eaa24db4c8ba32b58.mp4
                                //ResType = 0,                                  //媒体介质类型 0 视频 1 图片
                                RemoteRootDirectory = _siteConfig.RemotePath,     //远端路径 20240511  M3U8存储时，使用与上游一致的路径
                                ResSaveFullName = localPath,                        //本地文件存储路径 \\172.25.8.73\worksheet\FTP\Resource\Voole\Movie\202405\04\da982dd264d2cb6eaa24db4c8ba32b58.mp4
                                ResSaveDirectory = _runTask.LocalDirectory,   //本地文件保存目录 用于M3U8的TS文件做存储路径 \\172.25.8.73\worksheet\FTP\Resource\Voole\Movie\202405\04\
                                SiteURI = _siteConfig.ToString(),            //站点信息 ftp://huaweiftp:Mediafile123!@182.131.0.235:21 用于M3U8的TS文件做站点信息
                                BEnable = true
                            });
                            tsCount++;
                        }
                    }

                    WriteDebugMessageExt(LogOutName, "M3U8文件{0} 中ts文件数量为{1} 时长为{2}", url, tsCount, DataTransfer.FormatTimeStr(duration * 1000));
                }
                else
                {
                    throw new RunException(TransferCode.FileCheckErr, "非标准M3U8文件格式");
                }
            }
            else
            {
                throw new RunException(TransferCode.FileCheckErr, "非标准M3U8文件格式");
            }

        }
        /// <summary>
        /// 析构函数
        /// </summary>
        public void Dispose()
        {
            _downQueueFiles.Clear();
        }
    }
}
